/**
 * Copyright (C) 2017 - 2020 https://github.com/joewee
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.bifrost.wechat.service;

import com.bifrost.wechat.entity.WechatAccount;
import com.bifrost.wechat.exception.DuplicateServiceException;
import com.bifrost.wechat.handler.*;
import com.bifrost.wechat.repository.WechatAccountRepository;
import com.bifrost.wechat.service.impl.CbestWxMpService;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.mp.api.WxMpInMemoryConfigStorage;
import me.chanjar.weixin.mp.api.WxMpInRedisConfigStorage;
import me.chanjar.weixin.mp.api.WxMpMessageRouter;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
import me.chanjar.weixin.mp.constant.WxMpEventConstants;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import redis.clients.jedis.JedisPool;

import javax.annotation.PostConstruct;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 *
 *
 *@author joewee
 *@version 1.0.0
 *@Date 2018/2/22 10:54
 */
@Service
public class WxServiceContext {
    private static final Logger LOGGER= LoggerFactory.getLogger(WxServiceContext.class);
    private static final Map<String,WxMpService> wxServiceHolder =new ConcurrentHashMap<>(100);
    private static final Map<String,WxMpMessageRouter> routerHolder =new ConcurrentHashMap<>(100);
    private static final Map<String,Long> storeIdHolder =new ConcurrentHashMap<>(100);
    private static final Map<String,WxMpService> storeHolder=new ConcurrentHashMap<>(100);


    @Autowired
    private LocationHandler locationHandler;

    @Autowired
    private MenuHandler menuHandler;

    @Autowired
    private MsgHandler msgHandler;

    @Autowired
    protected LogHandler logHandler;

    @Autowired
    protected NullHandler nullHandler;

    @Autowired
    protected KfSessionHandler kfSessionHandler;

    @Autowired
    protected StoreCheckNotifyHandler storeCheckNotifyHandler;

    @Autowired
    private UnsubscribeHandler unsubscribeHandler;

    @Autowired
    private SubscribeHandler subscribeHandler;

    @Autowired
    JedisPool jedisPool;

    @Autowired
    WechatAccountRepository wechatAccountRepository;
    @Value("${cbest.accessKey:test_rrl}")
    private  String accessKey;
    @Value("${running.mode:production}")
    private String runningModel;
    @Value("${cbest.accessToken.url:null}")
    private String tokenUrl;
    @PostConstruct
    public synchronized void init(){
        clearAll();
        List<WechatAccount> accounts = wechatAccountRepository.findAll();
        if(accounts!=null&&!accounts.isEmpty()){
            accounts.forEach(wechatAccount -> {
                load(wechatAccount);
            });
        }
    }
    /**
     *
     * @param id appId
     * @return
     */
    public WxMpService getWxServiceByAppId(String id){
        if (StringUtils.isNotEmpty(id)) {
            if (wxServiceHolder.containsKey(id)) {
                return wxServiceHolder.get(id);
            }
            else{
               WechatAccount account = wechatAccountRepository.findFirstByAppId(id);
               if(account!=null){
                   return load(account);
               }
            }
        }
        throw new NoSuchBeanDefinitionException(String.format("未找到指定的对象：%s",id));
    }

    /**
     * 根据门店编码查询WxMpService
     * @param code
     * @return
     */
    public WxMpService getWxServiceByStoreCode(String code){
        if (StringUtils.isNotEmpty(code)) {
            if (storeHolder.containsKey(code)) {
                return storeHolder.get(code);
            }
            else{
                WechatAccount account = wechatAccountRepository.findFirstByStoreCode(code);
                if(account!=null){
                    return load(account);
                }
            }
        }
        throw new NoSuchBeanDefinitionException(String.format("未找到指定的对象：%s",code));
    }
    public void remove(String id){
        if (StringUtils.isNotEmpty(id)) {
            if (wxServiceHolder.containsKey(id)) {
                 wxServiceHolder.remove(id);
            }
            if(storeHolder.containsKey(id)){
                storeHolder.remove(id);
            }
        }
    }
    /**
     *
     * @param id appId
     * @return
     */
    public WxMpMessageRouter getWxMpMessageRouter(String id){
        if (StringUtils.isNotEmpty(id)) {
            if (routerHolder.containsKey(id)) {
                return routerHolder.get(id);
            }
        }
        throw new NoSuchBeanDefinitionException(String.format("未找到指定的对象：%s",id));
    }

    /**
     * 获取指定appid的对应门店号
     * @param id
     * @return
     */
    public Long getStoreId(String id){
        if (StringUtils.isNotEmpty(id)) {
            if (storeIdHolder.containsKey(id)) {
                return storeIdHolder.get(id);
            }
        }
        throw new RuntimeException(String.format("未找到appid为%s的门店信息",id));
    }

    private void clearAll() {
        wxServiceHolder.clear();
        routerHolder.clear();
        storeIdHolder.clear();
        storeHolder.clear();
    }

    /**
     * 加载相关服务
     * @param account
     * @return
     */
    private WxMpService load(WechatAccount account){
        WxMpServiceImpl wxMpService = null;
        if(!"production".equalsIgnoreCase(runningModel)){
            wxMpService = new CbestWxMpService(accessKey,tokenUrl);
        }else{
            wxMpService = new WxMpServiceImpl();
        }
        wxMpService.setWxMpConfigStorage(buildConfigStorage(account));
        wxServiceHolder.put(account.getAppId(),wxMpService);
        routerHolder.put(account.getAppId(),refreshRouter(wxMpService));
        storeIdHolder.put(account.getAppId(),account.getStoreId());
        storeHolder.put(account.getStoreCode(),wxMpService);
        return  wxMpService;
    }
    private WxMpInMemoryConfigStorage buildConfigStorage(WechatAccount wechatAccount){
        WxMpInMemoryConfigStorage  configStorage =null;
        if(jedisPool!=null){
            configStorage = new WxMpInRedisConfigStorage(jedisPool);
        }else{
            configStorage = new WxMpInMemoryConfigStorage();
        }
        configStorage.setAppId(wechatAccount.getAppId());
        configStorage.setSecret(wechatAccount.getAppSecret());
        configStorage.setToken(wechatAccount.getToken());
        configStorage.setAesKey(wechatAccount.getAesKey());
        return configStorage;
    }
    private WxMpMessageRouter refreshRouter(WxMpService wxMpService) {
        final WxMpMessageRouter newRouter = new WxMpMessageRouter(wxMpService);

        // 记录所有事件的日志 （异步执行）
        newRouter.rule().handler(this.logHandler).next();

        // 接收客服会话管理事件
        newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
                .event(WxMpEventConstants.CustomerService.KF_CREATE_SESSION)
                .handler(this.kfSessionHandler).end();
        newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
                .event(WxMpEventConstants.CustomerService.KF_CLOSE_SESSION)
                .handler(this.kfSessionHandler)
                .end();
        newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
                .event(WxMpEventConstants.CustomerService.KF_SWITCH_SESSION)
                .handler(this.kfSessionHandler).end();

        // 门店审核事件
        newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
                .event(WxMpEventConstants.POI_CHECK_NOTIFY)
                .handler(this.storeCheckNotifyHandler).end();

        // 自定义菜单事件
        newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
                .event(WxConsts.MenuButtonType.CLICK).handler(this.getMenuHandler()).end();

        // 点击菜单连接事件
        newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
                .event(WxConsts.MenuButtonType.VIEW).handler(this.nullHandler).end();

        // 关注事件
        newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
                .event(WxConsts.EventType.SUBSCRIBE).handler(this.getSubscribeHandler())
                .end();

        // 取消关注事件
        newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
                .event(WxConsts.EventType.UNSUBSCRIBE)
                .handler(this.getUnsubscribeHandler()).end();

        // 上报地理位置事件
        newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
                .event(WxConsts.EventType.LOCATION).handler(this.getLocationHandler())
                .end();

        // 接收地理位置消息
        newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.LOCATION)
                .handler(this.getLocationHandler()).end();

        // 扫码事件
        newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
                .event(WxConsts.EventType.SCAN).handler(this.getScanHandler()).end();

        // 默认
        newRouter.rule().async(false).handler(this.getMsgHandler()).end();

        return newRouter;
    }

    protected MenuHandler getMenuHandler() {
        return this.menuHandler;
    }

    protected SubscribeHandler getSubscribeHandler() {
        return this.subscribeHandler;
    }

    protected UnsubscribeHandler getUnsubscribeHandler() {
        return this.unsubscribeHandler;
    }

    protected AbstractHandler getLocationHandler() {
        return this.locationHandler;
    }

    protected MsgHandler getMsgHandler() {
        return this.msgHandler;
    }

    protected AbstractHandler getScanHandler() {
        return null;
    }
    @Deprecated
    private void addWxService(String id,WxMpService service){
        Assert.notNull(id,"id can not be null");
        Assert.notNull(service,"service can not be null");
        if (StringUtils.isNotEmpty(id)) {
            if (!wxServiceHolder.containsKey(id)) {
                wxServiceHolder.put(id,service);
            }else{
                throw new DuplicateServiceException(String.format("已经存在id为%s的对象",id));
            }
        }
    }
}
